﻿namespace Walson.Net.Sockets
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Security.Permissions;
    using Walson.IO;
    using System.Threading;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;

    /// <summary>
    /// 提供用户数据报 (UDP) 网络服务。
    /// </summary>
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(UdpClientCompact))]
    [DefaultEvent("DataReceived")]
    public class UdpClientCompact : Component ,IDisposable
    {
        private bool m_Active;
        private byte[] m_Buffer;
        private bool m_CleanedUp;
        private Socket m_ClientSocket;
        private AddressFamily m_Family=AddressFamily.InterNetwork;
        private bool m_IsBroadcast;
        private const int MaxUDPSize = 0x10000;
        // 输入数据流缓冲区。该数据流为一环形流，来自 Socket 的数据将写入此流中
        private RingFIFOMemoryStream m_BufferStream;
        private bool m_bTerminateReciever = false;
        private Queue<int> m_BufferMap = new Queue<int>();
        private Queue<IPEndPoint> m_IpEndPointList = new Queue<IPEndPoint>();
        object m_locker = new object();

        /// <summary>
        /// 事件到达处理方法
        /// </summary>
        /// <param name="sender">事件的发送方</param>
        /// <param name="e">参数</param>
        public delegate void DataReceivedHandler(object sender, EventArgs e);
        /// <summary>
        /// 数据到达事件
        /// </summary>
        public event DataReceivedHandler DataReceived;

        /// <summary>
        /// 指示组件是否正在运行
        /// </summary>
        public bool IsRunning
        {
            get;
            private set;
        }

        private string _Address = string.Empty;
        /// <summary>
        /// 本机 IP 地址
        /// </summary>
        public string Address
        {
            get
            {
                return _Address;
            }
            set
            {
                if (IsRunning)
                    throw (new Exception("组件正在运行。请停止组件后再进行设置。"));
                _Address = value;
            }
        }

        private int _Port = 0;
        /// <summary>
        /// 本机端口号
        /// </summary>
        public int Port
        {
            get
            {
                return _Port;
            }
            set
            {
                if (IsRunning)
                    throw (new Exception("组件正在运行。请停止组件后再进行设置。"));
                _Port = value;
            }
        }

        private bool _EnableAutoRecieve = true;
        /// <summary>
        /// 是否允许自动接收数据并以事件方式进行通知
        /// </summary>
        public bool EnableAutoRecieve
        {
            get
            {
                return _EnableAutoRecieve;
            }
            set
            {
                _EnableAutoRecieve = value;
            }
        }

        /// <summary>
        /// 原始消息的相关信息，包含原始消息字节数组和发消息的源的 IP 地址
        /// </summary>
        public class MessageInfo
        {
            /// <summary>
            /// 消息体
            /// </summary>
            public byte[] MessageBody;
            /// <summary>
            /// 发送方信息
            /// </summary>
            public IPEndPoint SenderInfo;
            /// <summary>
            /// 原始消息的相关信息
            /// </summary>
            /// <param name="RemoteIPEndpoint"></param>
            public MessageInfo(IPEndPoint RemoteIPEndpoint)
            {
                MessageBody = null;
                SenderInfo = RemoteIPEndpoint;
            }
        }

        private void FreeResources()
        {
            if (!this.m_CleanedUp)
            {
                m_BufferStream.Dispose();
                m_BufferStream = null;

                //Socket client = this.Client;
                if (this.Client != null)
                {
                    if (this.Client.Connected)
                        this.Client.Disconnect(true);
                    this.Client.Shutdown(SocketShutdown.Both);
                    this.Client.Close();
                    this.Client = null;
                }
                this.m_CleanedUp = true;
            }
            while (this.IsRunning)
                System.Windows.Forms.Application.DoEvents();
            this.IsRunning = false;
        }

        /// <summary>
        /// 启动组件
        /// </summary>
        /// <returns>启动是否成功</returns>
        public bool Start()
        {
            bool result = false;

            if (IsRunning == false)
            {
                m_BufferStream = new RingFIFOMemoryStream();

                this.m_CleanedUp = false;

                this.Client = new Socket(this.m_Family, SocketType.Dgram, ProtocolType.Udp);
                IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(_Address), _Port);

                this.Client = new Socket(this.m_Family, SocketType.Dgram, ProtocolType.Udp);

                //this.m_ClientSocket.MulticastLoopback = _MulticastLoopback;
                //this.m_ClientSocket.DontFragment = _DontFragment;
                this.m_ClientSocket.EnableBroadcast = _EnableBroadcast;
                this.m_ClientSocket.ExclusiveAddressUse = _ExclusiveAddressUse;
                //this.m_ClientSocket.Ttl = _Ttl;

                this.Client.Bind(localEP);
                
                this.IsRunning = false;

                this.m_Buffer = new byte[0x10000];

                if (EnableAutoRecieve)
                    StartAutoReceive();

                result = true;
            }

            return result;
        }

        /// <summary>
        /// 停止组件运行
        /// </summary>
        public void Stop()
        {

            if (IsRunning == true)
            {
                m_bTerminateReciever = true;

                FreeResources();
            }
        }



        /// <summary>
        /// 初始化 UdpClientCompact 类的新实例，并将它绑定到所提供的本地 IP 地址和端口号。
        /// </summary>
        /// <param name="ipAddress">本地 IP 地址</param>
        /// <param name="port">本地端口号</param>
        public UdpClientCompact(string ipAddress, int port)
        {
            _Address = ipAddress;
            _Port = port;
        }

        /// <summary>
        /// 初始化 UdpClientCompact 类的新实例，并将它绑定到默认的本地 IP 地址和指定的端口号。
        /// </summary>
        /// <param name="port">本地端口号</param>
        public UdpClientCompact(int port)
            : this(GetLocalIp(), port)
        {
        }

        /// <summary>
        /// 初始化 UdpClientCompact 类的新实例，并将它绑定到默认的本地 IP 地址和端口号。
        /// </summary>
        public UdpClientCompact()
            : this(GetLocalIp(), 9000)
        {
        }

        static private string GetLocalIp()
        {
            IPAddress[] addr = Dns.GetHostAddresses(Dns.GetHostName());
            string ipAddress = "127.0.0.1";

            foreach (IPAddress ip in addr)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    ipAddress = ip.ToString();
                    break;
                }
            }

            return ipAddress;
        }

        public string RefreshAddress()
        {
            return GetLocalIp();
        }


        private void CheckForBroadcast(IPAddress ipAddress)
        {
            if (((this.Client != null) && !this.m_IsBroadcast) && ipAddress.Equals(IPAddress.Parse("255.255.255.255")))
            {
                this.m_IsBroadcast = true;
                this.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            }
        }

        /// <summary>
        /// 释放由 UdpClientCompact 占用的非托管资源，还可以另外再释放托管资源。 
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.FreeResources();
                GC.SuppressFinalize(this);
            }
            base.Dispose(true);
        }


      

        /// <summary>
        /// 启动自动接收。在收到数据后会通过 DataRecieved 事件报告给使用者。启动自动接收前必须先注册 DataReceived 事件，
        /// 否则将会抛出异常。
        /// </summary>
        private void StartAutoReceive()
        {
            if (this.IsRunning)
                return;

            if (this.DataReceived == null)
            {
                throw new Exception("没有指定 DataReceived 事件处理方法。");
            }

            Thread thd = new Thread(new ThreadStart(ThdReceive));
            thd.IsBackground = true;
            thd.Priority = ThreadPriority.Highest;

            m_bTerminateReciever = false;
            this.m_CleanedUp = false;
            this.IsRunning = true;
            thd.Start();
        }

        /// <summary>
        /// 停止自动接收。
        /// </summary>
        public void StopAutoRecieve()
        {
            m_bTerminateReciever = true;
        }

        private void ThdReceive()
        {
            EndPoint any = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));

            int receivedBytes = 0;
            byte[] m_byteRawDgram;

            while (m_bTerminateReciever == false)
            {
                if (this.Client.Available == 0)
                {
                    Thread.Sleep(1);
                    continue;
                }

                m_byteRawDgram = new byte[this.Client.Available];

                if (m_byteRawDgram.Length == 0)
                {
                    Thread.Sleep(1);
                    continue;
                }

                try
                {
                    receivedBytes = this.Client.ReceiveFrom(m_byteRawDgram, ref any);
                    lock (m_locker)
                    {
                        m_BufferMap.Enqueue(receivedBytes);
                        m_IpEndPointList.Enqueue(any as IPEndPoint);
                        m_BufferStream.Write(m_byteRawDgram, 0, receivedBytes);
                    }
                   

                    if (DataReceived != null)
                        DataReceived(this, new EventArgs());

                }
                catch (Exception ex)
                {
                    string str = ex.Message;
                    continue;
                }
            }
            this.IsRunning = false;
        }

        /// <summary>
        /// 从缓冲区中读取消息
        /// </summary>
        /// <returns>读取的消息</returns>
        public MessageInfo ReadMessage()
        {
            MessageInfo msg;
            lock (m_locker)
            {
                int Length = m_BufferMap.Dequeue();
                byte[] temp = new byte[Length];
                m_BufferStream.Read(temp, 0, Length);
                msg = new MessageInfo(m_IpEndPointList.Dequeue());
                msg.MessageBody = temp;
            }
            return msg;
        }

        /// <summary>
        /// 清除已接收的所有消息和数据
        /// </summary>
        public void Clear()
        {
            lock (m_locker)
            {
                EndPoint any = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));

                byte[] buffer = new byte[m_BufferStream.Length];
                m_BufferMap.Clear();
                m_IpEndPointList.Clear();
                m_BufferStream.Read(buffer, 0, buffer.Length);
                if (this.Client.Available > 0)
                {
                    buffer = new byte[this.Client.Available];
                    this.Client.ReceiveFrom(buffer, ref any);
                }
            }
            
        }

        /// <summary>
        /// 返回已由远程主机发送的 UDP 数据报。
        /// </summary>
        /// <param name="remoteEP">一个 IPEndPoint，它表示从其发送数据的远程主机。</param>
        /// <returns>一个类型为 Byte 的数组，它包含数据报数据。 </returns>
        public byte[] Receive(ref IPEndPoint remoteEP)
        {
            EndPoint any = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));
            if (this.m_CleanedUp)
            {
                throw new ObjectDisposedException(base.GetType().FullName);
            }

            int count = this.Client.ReceiveFrom(this.m_Buffer, 0x10000, SocketFlags.None, ref any);
            remoteEP = (IPEndPoint) any;
            if (count < 0x10000)
            {
                byte[] dst = new byte[count];
                Buffer.BlockCopy(this.m_Buffer, 0, dst, 0, count);
                return dst;
            }
            return this.m_Buffer;
        }

        /// <summary>
        /// 向指定主机发送 UDP 数据报。
        /// </summary>
        /// <param name="ipAddress">远端主机 IP 地址</param>
        /// <param name="port">远端主机端口号</param>
        /// <param name="dgram">Byte 类型的数组，它指定您打算发送的 UDP 数据报，表示为字节数组。 </param>
        /// <param name="bytes">数据报中的字节数。</param>
        /// <returns>已发送的字节数。</returns>
        public int SendTo(string ipAddress, int port, byte[] dgram, int bytes)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
            this.CheckForBroadcast(endPoint.Address);
            return this.Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint);
        }

        /// <summary>
        /// 向指定主机发送 UDP 数据报。
        /// </summary>
        /// <param name="ipAddress">远端主机 IP 地址</param>
        /// <param name="port">远端主机端口号</param>
        /// <param name="dgram">Byte 类型的数组，它指定您打算发送的 UDP 数据报，表示为字节数组。</param>
        /// <returns>已发送的字节数。</returns>
        public int SendTo(string ipAddress, int port, byte[] dgram)
        {
            return SendTo(ipAddress, port, dgram, dgram.Length);
        }

        /// <summary>
        /// 向指定主机发送 UDP 数据报。
        /// </summary>
        /// <param name="ipAddress">远端主机 IP 地址</param>
        /// <param name="port">远端主机端口号</param>
        /// <param name="str">字符串类型的数据，它指定您打算作为 UDP 数据报发送的字符串。</param>
        /// <returns>已发送的字节数。</returns>
        public int SendTo(string ipAddress, int port, string str)
        {
            byte[] dgram = System.Text.Encoding.Default.GetBytes(str);
            return SendTo(ipAddress, port, dgram, dgram.Length);
        }

        /// <summary>
        /// 向指定主机发送 UDP 数据报。
        /// </summary>
        /// <param name="ipAddress">远端主机 IP 地址</param>
        /// <param name="port">远端主机端口号</param>
        /// <param name="structOrClass">object 类型的数据，可以是一个结构或类，它指定您打算作为 UDP 数据报发送的内容，该内容将被自动转换为字节数组发送。</param>
        /// <returns>已发送的字节数。</returns>
        public int SendTo(string ipAddress, int port, object structOrClass)
        {
            byte[] dgram = Walson.Tools.Converter.StructToBytes(structOrClass);
            return SendTo(ipAddress, port, dgram, dgram.Length);
        }

        /// <summary>
        /// 向网络广播 UDP 数据报
        /// </summary>
        /// <param name="port">远端主机端口号</param>
        /// <param name="dgram">Byte 类型的数组，它指定您打算发送的 UDP 数据报，表示为字节数组。</param>
        /// <param name="bytes">数据报中的字节数。</param>
        /// <returns>已发送的字节数。</returns>
        public int Broadcast(int port, byte[] dgram, int bytes)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, port);
            this.m_IsBroadcast = true;
            this.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            return this.Client.SendTo(dgram, 0, bytes, SocketFlags.None, endPoint);
        }

        /// <summary>
        /// 向网络广播 UDP 数据报
        /// </summary>
        /// <param name="port">远端主机端口号</param>
        /// <param name="dgram">Byte 类型的数组，它指定您打算发送的 UDP 数据报，表示为字节数组。</param>
        /// <returns>已发送的字节数。</returns>
        public int Broadcast(int port, byte[] dgram)
        {
            return Broadcast(port, dgram, dgram.Length);
        }
        
        /// <summary>
        /// 向网络广播 UDP 数据报
        /// </summary>
        /// <param name="port">远端主机端口号</param>
        /// <param name="structOrClass">object 类型的数据，可以是一个结构或类，它指定您打算作为 UDP 数据报发送的内容，该内容将被自动转换为字节数组发送。</param>
        /// <returns>已发送的字节数。</returns>
        public int Broadcast(int port, object structOrClass)
        {
            byte[] dgram = Walson.Tools.Converter.StructToBytes(structOrClass);
            return Broadcast(port, dgram, dgram.Length);
        }




        void IDisposable.Dispose()
        {
            this.Dispose(true);
        }

        protected bool Active
        {
            get
            {
                return this.m_Active;
            }
            set
            {
                this.m_Active = value;
            }
        }

        /// <summary>
        /// 获取从网络接收的可读取的数据量。
        /// </summary>
        [Browsable(false)]
        public int BytesAvailable
        {
            get
            {
                if (this.m_ClientSocket == null)
                    return 0;
                else
                    return this.m_ClientSocket.Available;
            }
        }

        /// <summary>
        /// 可供读取的数据包数量
        /// </summary>
        [Browsable(false)]
        public int MessageAvailable
        {
            get 
            {
                return m_BufferMap.Count;
            }
        }

        /// <summary>
        /// 获取或设置基础网络 Socket。
        /// </summary>
        public Socket Client
        {
            get
            {
                return this.m_ClientSocket;
            }
            private set
            {
                this.m_ClientSocket = value;
            }
        }

//         bool _DontFragment = false;
//         /// <summary>
//         /// 获取或设置 Boolean 值，指定 UdpClient 是否允许对 Internet 协议 (IP) 数据报进行分段。
//         /// </summary>
//         public bool DontFragment
//         {
//             get
//             {
//                 return _DontFragment;
//                 //return this.m_ClientSocket.DontFragment;
//             }
//             set
//             {
//                 _DontFragment = value;
//                 //this.m_ClientSocket.DontFragment = value;
//             }
//         }

        private bool _EnableBroadcast = false;
        /// <summary>
        /// 获取或设置 Boolean 值，指定 UdpClient 是否可以发送或接收广播数据包。
        /// </summary>
        public bool EnableBroadcast
        {
            get
            {
                return _EnableBroadcast;
                //return this.m_ClientSocket.EnableBroadcast;
            }
            set
            {
                _EnableBroadcast = value;
                //this.m_ClientSocket.EnableBroadcast = value;
            }
        }

        private bool _ExclusiveAddressUse = false;
        /// <summary>
        /// 获取或设置 Boolean 值，指定 UdpClient 是否只允许一个客户端使用端口。
        /// </summary>
        public bool ExclusiveAddressUse
        {
            get
            {
                return _ExclusiveAddressUse;
                //return this.m_ClientSocket.ExclusiveAddressUse;
            }
            set
            {
                _ExclusiveAddressUse = value;
                //this.m_ClientSocket.ExclusiveAddressUse = value;
            }
        }

//         bool _MulticastLoopback = false;
//         /// <summary>
//         /// 获取或设置 Boolean 值，指定是否将输出多路广播数据包传递给发送应用程序。
//         /// </summary>
//         public bool MulticastLoopback
//         {
//             get
//             {
//                 return _MulticastLoopback;
//                 //return this.m_ClientSocket.MulticastLoopback;
//             }
//             set
//             {
//                 _MulticastLoopback = value;
//                 //this.m_ClientSocket.MulticastLoopback = value;
//             }
//         }

//         short _Ttl=64;
//         /// <summary>
//         /// 获取或设置一个值，指定由 UdpClient 发送的 Internet 协议 (IP) 数据包的生存时间 (TTL)。
//         /// </summary>
//         public short Ttl
//         {
//             get
//             {
//                 //return this.m_ClientSocket.Ttl;
//                 return _Ttl;
//             }
//             set
//             {
//                 this._Ttl = value;
//                 //this.m_ClientSocket.Ttl = value;
//             }
//         }
    }
}

